home *** CD-ROM | disk | FTP | other *** search
/ SGI Developer Toolbox 6.1 / SGI Developer Toolbox 6.1 - Disc 1.iso / toolbox / src / exampleCode / opengl / GLUT / lib / glut / glut_win.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-11-11  |  27.7 KB  |  986 lines

  1. /* Copyright (c) Mark J. Kilgard, 1994.  */
  2.  
  3. /* This program is freely distributable without licensing fees
  4.    and is provided without guarantee or warrantee expressed or
  5.    implied. This program is -not- in the public domain. */
  6.  
  7. #include <stdlib.h>
  8. #include <stdio.h>
  9. #include <string.h>
  10. #include <assert.h>
  11. #include <X11/Xlib.h>
  12. #include <X11/Xutil.h>
  13. #include <X11/Xatom.h>  /* for XA_RGB_DEFAULT_MAP atom */
  14. #if defined(__vms)
  15. #include <X11/StdCmap.h>  /* for XmuLookupStandardColormap */
  16. #else
  17. #include <X11/Xmu/StdCmap.h>  /* for XmuLookupStandardColormap */
  18. #endif
  19. #include <GL/glut.h>
  20. #include "glutint.h"
  21.  
  22. GLUTwindow *__glutCurrentWindow = NULL;
  23. GLUTwindow **__glutWindowList = NULL;
  24. int __glutWindowListSize = 0;
  25. GLUTstale *__glutStaleWindowList = NULL;
  26.  
  27. void (*__glutFreeOverlayFunc) (GLUToverlay *);
  28.  
  29. static void
  30. cleanWindowWorkList(GLUTwindow * window)
  31. {
  32.   GLUTwindow **pEntry = &__glutWindowWorkList;
  33.   GLUTwindow *entry = __glutWindowWorkList;
  34.  
  35.   /* Tranverse singly-linked window work list look for the
  36.      window. */
  37.   while (entry) {
  38.     if (entry == window) {
  39.       /* Found it; delete it. */
  40.       *pEntry = entry->prevWorkWin;
  41.       return;
  42.     } else {
  43.       pEntry = &entry->prevWorkWin;
  44.       entry = *pEntry;
  45.     }
  46.   }
  47. }
  48.  
  49. static void
  50. cleanStaleWindowList(GLUTwindow * window)
  51. {
  52.   GLUTstale **pEntry = &__glutStaleWindowList;
  53.   GLUTstale *entry = __glutStaleWindowList;
  54.  
  55.   /* Tranverse singly-linked stale window list look for the
  56.      window ID. */
  57.   while (entry) {
  58.     if (entry->window == window) {
  59.       /* Found it; delete it. */
  60.       *pEntry = entry->next;
  61.       free(entry);
  62.       return;
  63.     } else {
  64.       pEntry = &entry->next;
  65.       entry = *pEntry;
  66.     }
  67.   }
  68. }
  69.  
  70. static GLUTwindow *__glutWindowCache = NULL;
  71.  
  72. GLUTwindow *
  73. __glutGetWindow(Window win)
  74. {
  75.   GLUTstale *entry;
  76.   int i;
  77.  
  78.   /* Does win belong to the last window ID looked up? */
  79.   if (__glutWindowCache && (win == __glutWindowCache->win ||
  80.       (__glutWindowCache->overlay && win ==
  81.         __glutWindowCache->overlay->win))) {
  82.     return
  83.       __glutWindowCache;
  84.   }
  85.   /* Otherwise scan the window list looking for the window ID. */
  86.   for (i = 0; i < __glutWindowListSize; i++) {
  87.     if (__glutWindowList[i]) {
  88.       if (win == __glutWindowList[i]->win) {
  89.         __glutWindowCache = __glutWindowList[i];
  90.         return __glutWindowCache;
  91.       }
  92.       if (__glutWindowList[i]->overlay) {
  93.         if (win == __glutWindowList[i]->overlay->win) {
  94.           __glutWindowCache = __glutWindowList[i];
  95.           return __glutWindowCache;
  96.         }
  97.       }
  98.     }
  99.   }
  100.   /* Scan through destroyed overlay window IDs for which no
  101.      DestroyNotify has yet been received. */
  102.   for (entry = __glutStaleWindowList; entry; entry = entry->next) {
  103.     if (entry->win == win)
  104.       return entry->window;
  105.   }
  106.   return NULL;
  107. }
  108.  
  109. /* CENTRY */
  110. int
  111. glutGetWindow(void)
  112. {
  113.   if (__glutCurrentWindow) {
  114.     return __glutCurrentWindow->num + 1;
  115.   } else {
  116.     return 0;
  117.   }
  118. }
  119. /* ENDCENTRY */
  120.  
  121. void
  122. __glutSetWindow(GLUTwindow * window)
  123. {
  124.   /* It is tempting to try to short-circuit the call to
  125.      glXMakeCurrent if we "know" we are going to make current
  126.      to a window we are already current to.  In fact, this
  127.      assumption breaks when GLUT is expected to integrated with
  128.      other OpenGL windowing APIs that also make current to
  129.      OpenGL contexts.  Since glXMakeCurrent short-circuits the
  130.      "already bound" case, GLUT avoids the temptation to do so
  131.      too. */
  132.   __glutCurrentWindow = window;
  133.   glXMakeCurrent(__glutDisplay, __glutCurrentWindow->renderWin,
  134.     __glutCurrentWindow->renderCtx);
  135.  
  136.   /* We should be careful to force a finish between each
  137.      iteration through the GLUT main loop if indirect OpenGL 
  138.      contexts are in use; indirect contexts tend to have  much
  139.      longer latency because lots of OpenGL extension requests
  140.      can queue up in the X protocol stream.  We accomplish this
  141.      by posting GLUT_FINISH_WORK to be done. */
  142.   if (!__glutCurrentWindow->isDirect)
  143.     __glutPutOnWorkList(__glutCurrentWindow, GLUT_FINISH_WORK);
  144.  
  145.   /* If debugging is enabled, we'll want to check this window
  146.      for any OpenGL errors every iteration through the GLUT
  147.      main loop.  To accomplish this, we post the
  148.      GLUT_DEBUG_WORK to be done on this window. */
  149.   if (__glutDebug)
  150.     __glutPutOnWorkList(__glutCurrentWindow, GLUT_DEBUG_WORK);
  151. }
  152.  
  153. /* CENTRY */
  154. void
  155. glutSetWindow(int win)
  156. {
  157.   GLUTwindow *window;
  158.  
  159.   if (win < 1 || win > __glutWindowListSize) {
  160.     __glutWarning("glutWindowSet attempted on bogus window.");
  161.     return;
  162.   }
  163.   window = __glutWindowList[win - 1];
  164.   if (!window) {
  165.     __glutWarning("glutWindowSet attempted on bogus window.");
  166.     return;
  167.   }
  168.   __glutSetWindow(window);
  169. }
  170. /* ENDCENTRY */
  171.  
  172. static int
  173. getUnusedWindowSlot(void)
  174. {
  175.   int i;
  176.  
  177.   /* Look for allocated, unused slot. */
  178.   for (i = 0; i < __glutWindowListSize; i++) {
  179.     if (!__glutWindowList[i]) {
  180.       return i;
  181.     }
  182.   }
  183.   /* Allocate a new slot. */
  184.   __glutWindowListSize++;
  185.   if (__glutWindowList) {
  186.     __glutWindowList = (GLUTwindow **)
  187.       realloc(__glutWindowList,
  188.       __glutWindowListSize * sizeof(GLUTwindow *));
  189.   } else {
  190.     /* XXX Some realloc's do not correctly perform a malloc
  191.        when asked to perform a realloc on a NULL pointer,
  192.        though the ANSI C library spec requires this. */
  193.     __glutWindowList = (GLUTwindow **)
  194.       malloc(sizeof(GLUTwindow *));
  195.   }
  196.   if (!__glutWindowList)
  197.     __glutFatalError("out of memory.");
  198.   __glutWindowList[__glutWindowListSize - 1] = NULL;
  199.   return __glutWindowListSize - 1;
  200. }
  201.  
  202. static XVisualInfo *
  203. getVisualInfoCI(unsigned int mode)
  204. {
  205.   static int bufSizeList[] =
  206.   {16, 12, 8, 4, 2, 1, 0};
  207.   XVisualInfo *vi;
  208.   int list[32];
  209.   int i, n = 0;
  210.  
  211.   list[n++] = GLX_BUFFER_SIZE;
  212.   list[n++] = 1;
  213.   if (GLUT_WIND_IS_DOUBLE(mode)) {
  214.     list[n++] = GLX_DOUBLEBUFFER;
  215.   }
  216.   if (GLUT_WIND_IS_STEREO(mode)) {
  217.     list[n++] = GLX_STEREO;
  218.   }
  219.   if (GLUT_WIND_HAS_DEPTH(mode)) {
  220.     list[n++] = GLX_DEPTH_SIZE;
  221.     list[n++] = 1;
  222.   }
  223.   if (GLUT_WIND_HAS_STENCIL(mode)) {
  224.     list[n++] = GLX_STENCIL_SIZE;
  225.     list[n++] = 1;
  226.   }
  227.   list[n] = (int) None; /* terminate list */
  228.  
  229.   /* glXChooseVisual specify GLX_BUFFER_SIZE prefers the
  230.      "smallest index buffer of at least the specified size".
  231.      This would be reasonable if GLUT allowed the user to
  232.      specify the required buffe size, but GLUT's display mode
  233.      is too simplistic (easy to use?). GLUT should try to find
  234.      the "largest".  So start with a large buffer size and
  235.      shrink until we find a matching one that exists. */
  236.  
  237.   for (i = 0; bufSizeList[i]; i++) {
  238.     /* XXX Assumes list[1] is where GLX_BUFFER_SIZE parameter
  239.        is. */
  240.     list[1] = bufSizeList[i];
  241.     vi = glXChooseVisual(__glutDisplay,
  242.       __glutScreen, list);
  243.     if (vi)
  244.       return vi;
  245.   }
  246.   return NULL;
  247. }
  248.  
  249. static XVisualInfo *
  250. getVisualInfoRGB(unsigned int mode)
  251. {
  252.   int list[32];
  253.   int n = 0;
  254.  
  255.   /* XXX Would a caching mechanism to minize the calls to
  256.      glXChooseVisual? You'd have to reference count
  257.      XVisualInfo* pointers. */
  258.  
  259.   list[n++] = GLX_RGBA;
  260.   list[n++] = GLX_RED_SIZE;
  261.   list[n++] = 1;
  262.   list[n++] = GLX_GREEN_SIZE;
  263.   list[n++] = 1;
  264.   list[n++] = GLX_BLUE_SIZE;
  265.   list[n++] = 1;
  266.   if (GLUT_WIND_HAS_ALPHA(mode)) {
  267.     list[n++] = GLX_ALPHA_SIZE;
  268.     list[n++] = 1;
  269.   }
  270.   if (GLUT_WIND_IS_DOUBLE(mode)) {
  271.     list[n++] = GLX_DOUBLEBUFFER;
  272.   }
  273.   if (GLUT_WIND_IS_STEREO(mode)) {
  274.     list[n++] = GLX_STEREO;
  275.   }
  276.   if (GLUT_WIND_HAS_DEPTH(mode)) {
  277.     list[n++] = GLX_DEPTH_SIZE;
  278.     list[n++] = 1;
  279.   }
  280.   if (GLUT_WIND_HAS_STENCIL(mode)) {
  281.     list[n++] = GLX_STENCIL_SIZE;
  282.     list[n++] = 1;
  283.   }
  284.   if (GLUT_WIND_HAS_ACCUM(mode)) {
  285.     list[n++] = GLX_ACCUM_RED_SIZE;
  286.     list[n++] = 1;
  287.     list[n++] = GLX_ACCUM_GREEN_SIZE;
  288.     list[n++] = 1;
  289.     list[n++] = GLX_ACCUM_BLUE_SIZE;
  290.     list[n++] = 1;
  291.     if (GLUT_WIND_HAS_ALPHA(mode)) {
  292.       list[n++] = GLX_ACCUM_ALPHA_SIZE;
  293.       list[n++] = 1;
  294.     }
  295.   }
  296. #if defined(GLX_VERSION_1_1) && defined(GLX_SGIS_multisample)
  297.   if (GLUT_WIND_IS_MULTISAMPLE(mode)) {
  298.     if (!__glutIsSupportedByGLX("GLX_SGIS_multisample"))
  299.       return NULL;
  300.     list[n++] = GLX_SAMPLES_SGIS;
  301.     /* XXX Is 4 a reasonable minimum acceptable number of
  302.        samples? */
  303.     list[n++] = 4;
  304.   }
  305. #endif
  306.   list[n] = (int) None; /* terminate list */
  307.  
  308.   return glXChooseVisual(__glutDisplay,
  309.     __glutScreen, list);
  310. }
  311.  
  312. XVisualInfo *
  313. __glutGetVisualInfo(unsigned int mode)
  314. {
  315.   /* XXX GLUT_LUMINANCE not implemented for GLUT 3.0. */
  316.   if (GLUT_WIND_IS_LUMINANCE(mode))
  317.     return NULL;
  318.  
  319.   if (GLUT_WIND_IS_RGB(mode))
  320.     return getVisualInfoRGB(mode);
  321.   else
  322.     return getVisualInfoCI(mode);
  323. }
  324.  
  325. XVisualInfo *
  326. __glutDetermineVisual(
  327.   unsigned int displayMode,
  328.   Bool * singleFake,
  329.   XVisualInfo * (getVisualInfo) (unsigned int))
  330. {
  331.   XVisualInfo *vis;
  332.  
  333.   *singleFake = False;
  334.   vis = getVisualInfo(displayMode);
  335.   if (!vis) {
  336.     /* Fallback cases when can't get exactly what was asked
  337.        for... */
  338.     if (GLUT_WIND_IS_SINGLE(displayMode)) {
  339.       /* If we can't find a single buffered visual, try looking
  340.          for a double buffered visual.  We can treat a double
  341.          buffered visual as a single buffer visual by changing
  342.          the draw buffer to GL_FRONT and treating any swap
  343.          buffers as no-ops. */
  344.       displayMode |= GLUT_DOUBLE;
  345.       vis = getVisualInfo(displayMode);
  346.       *singleFake = True;
  347.     }
  348.     if (!vis && GLUT_WIND_IS_MULTISAMPLE(displayMode)) {
  349.       /* If we can't seem to get multisampling (ie, not Reality
  350.          Engine class graphics!), go without multisampling.  It
  351.          is up to the application to query how many multisamples
  352.          were allocated (0 equals no multisampling) if the
  353.          application is going to use multisampling for more than
  354.          just antialiasing. */
  355.       displayMode &= ~GLUT_MULTISAMPLE;
  356.       vis = getVisualInfo(displayMode);
  357.     }
  358.   }
  359.   return vis;
  360. }
  361.  
  362. void
  363. __glutSetupColormap(XVisualInfo * vi, GLUTcolormap ** colormap, Colormap * cmap)
  364. {
  365.   Status status;
  366.   XStandardColormap *standardCmaps;
  367.   int i, numCmaps;
  368.  
  369.   switch (vi->class) {
  370.   case PseudoColor:
  371.     if (GLUT_WIND_IS_RGB(__glutDisplayMode)) {
  372.       /* Mesa might return a PseudoColor visual for RGB mode. */
  373.       *colormap = NULL;
  374.       if (MaxCmapsOfScreen(DefaultScreenOfDisplay(__glutDisplay)) == 1
  375.         && vi->visual == DefaultVisual(__glutDisplay, __glutScreen)) {
  376.         char *private = getenv("MESA_PRIVATE_CMAP");
  377.  
  378.         if (private) {
  379.           /* User doesn't want to share colormaps. */
  380.           *cmap = XCreateColormap(__glutDisplay, __glutRoot,
  381.             vi->visual, AllocNone);
  382.         } else {
  383.           /* Share the root colormap. */
  384.           *cmap = DefaultColormap(__glutDisplay, __glutScreen);
  385.         }
  386.       } else {
  387.         /* Get our own PseudoColor colormap. */
  388.         *cmap = XCreateColormap(__glutDisplay, __glutRoot,
  389.           vi->visual, AllocNone);
  390.       }
  391.     } else {
  392.       /* CI mode, real GLX never returns a PseudoColor visual
  393.          for RGB mode. */
  394.       *colormap = __glutAssociateColormap(vi);
  395.       *cmap = (*colormap)->cmap;
  396.     }
  397.     break;
  398.   case TrueColor:
  399.   case DirectColor:
  400.     *colormap = NULL;   /* NULL if RGBA */
  401. #ifndef SOLARIS_2_4_BUG
  402.     /* Solaris 2.4 has a bug in its XmuLookupStandardColormap
  403.        implementation.  Please compile your Solaris 2.4 version 
  404.        of GLUT with -DSOLARIS_2_4_BUG to work around this bug.
  405.        The symptom of the bug is that programs will get a
  406.        BadMatch error from X_CreateWindow when creating a GLUT
  407.        window because Solaris 2.4 creates a  corrupted
  408.        RGB_DEFAULT_MAP property.  Note that this workaround
  409.        prevents Colormap sharing between applications, perhaps
  410.        leading unnecessary colormap installations or colormap
  411.        flashing. */
  412.     status = XmuLookupStandardColormap(__glutDisplay,
  413.       vi->screen, vi->visualid, vi->depth, XA_RGB_DEFAULT_MAP,
  414.       /* replace */ False, /* retain */ True);
  415.     if (status == 1) {
  416.       status = XGetRGBColormaps(__glutDisplay, __glutRoot,
  417.         &standardCmaps, &numCmaps, XA_RGB_DEFAULT_MAP);
  418.       if (status == 1)
  419.         for (i = 0; i < numCmaps; i++)
  420.           if (standardCmaps[i].visualid == vi->visualid) {
  421.             *cmap = standardCmaps[i].colormap;
  422.             XFree(standardCmaps);
  423.             return;
  424.           }
  425.     }
  426. #endif
  427.     /* If no standard colormap but TrueColor, just make a
  428.        private one. */
  429.     /* XXX Should do a better job of internal sharing for
  430.        privately allocated TrueColor colormaps. */
  431.     /* XXX DirectColor probably needs ramps hand initialized! */
  432.     *cmap = XCreateColormap(__glutDisplay, __glutRoot,
  433.       vi->visual, AllocNone);
  434.     break;
  435.   case StaticColor:
  436.   case StaticGray:
  437.   case GrayScale:
  438.     /* Mesa supports these visuals */
  439.     *colormap = NULL;
  440.     *cmap = XCreateColormap(__glutDisplay, __glutRoot,
  441.       vi->visual, AllocNone);
  442.     break;
  443.   default:
  444.     __glutFatalError(
  445.       "could not allocate colormap for visual type: %d.",
  446.       vi->class);
  447.   }
  448.   return;
  449. }
  450.  
  451. void
  452. __glutDefaultDisplay(void)
  453. {
  454.   /* XXX Remove the warning after GLUT 3.0. */
  455.   __glutWarning("The following is a new check for GLUT 3.0; update your code.");
  456.   __glutFatalError(
  457.     "display needed for window %d, but no display callback.",
  458.     __glutCurrentWindow->num);
  459. }
  460.  
  461. void
  462. __glutDefaultReshape(int width, int height)
  463. {
  464.   GLUToverlay *overlay;
  465.  
  466.   /* Adjust the viewport of the window (and overlay if one
  467.      exists). */
  468.   glXMakeCurrent(__glutDisplay, __glutCurrentWindow->win,
  469.     __glutCurrentWindow->ctx);
  470.   glViewport(0, 0, (GLsizei) width, (GLsizei) height);
  471.   overlay = __glutCurrentWindow->overlay;
  472.   if (overlay) {
  473.     glXMakeCurrent(__glutDisplay, overlay->win, overlay->ctx);
  474.     glViewport(0, 0, (GLsizei) width, (GLsizei) height);
  475.   }
  476.   /* Make sure we are current to the current layer (application 
  477.  
  478.      should be able to count on the current layer not changing
  479.      unless the application explicitly calls glutUseLayer). */
  480.   glXMakeCurrent(__glutDisplay, __glutCurrentWindow->renderWin,
  481.     __glutCurrentWindow->renderCtx);
  482. }
  483.  
  484. GLUTwindow *
  485. __glutCreateWindow(GLUTwindow * parent,
  486.   int x, int y, int width, int height)
  487. {
  488.   GLUTwindow *window;
  489.   XSetWindowAttributes wa;
  490.   unsigned long attribMask;
  491.   int winnum;
  492.   int i;
  493.  
  494.   if (!__glutDisplay)
  495.     __glutOpenXConnection(NULL);
  496.   winnum = getUnusedWindowSlot();
  497.   window = (GLUTwindow *) malloc(sizeof(GLUTwindow));
  498.   if (!window)
  499.     __glutFatalError("out of memory.");
  500.   window->num = winnum;
  501.  
  502.   window->vis = __glutDetermineVisual(__glutDisplayMode,
  503.     &window->fakeSingle, __glutGetVisualInfo);
  504.   if (!window->vis) {
  505.     __glutFatalError(
  506.       "visual with necessary capabilities not found.");
  507.   }
  508.   window->ctx = glXCreateContext(__glutDisplay, window->vis,
  509.     None, __glutTryDirect);
  510.   window->renderCtx = window->ctx;
  511.   window->isDirect = glXIsDirect(__glutDisplay, window->ctx);
  512.   if (__glutForceDirect) {
  513.     if (!window->isDirect)
  514.       __glutFatalError("direct rendering not possible.");
  515.   }
  516.   __glutSetupColormap(window->vis, &(window->colormap), &(window->cmap));
  517.   window->eventMask = StructureNotifyMask | ExposureMask;
  518.  
  519.   attribMask = CWBackPixmap | CWBorderPixel | CWColormap | CWEventMask;
  520.   wa.background_pixmap = None;
  521.   wa.border_pixel = 0;
  522.   wa.colormap = window->cmap;
  523.   wa.event_mask = window->eventMask;
  524.   if (parent) {
  525.     if (parent->eventMask & GLUT_HACK_STOP_PROPAGATE_MASK)
  526.       wa.event_mask |= GLUT_HACK_STOP_PROPAGATE_MASK;
  527.     attribMask |= CWDontPropagate;
  528.     wa.do_not_propagate_mask = parent->eventMask & GLUT_DONT_PROPAGATE_FILTER_MASK;
  529.   } else {
  530.     wa.do_not_propagate_mask = 0;
  531.   }
  532.   window->win = XCreateWindow(__glutDisplay,
  533.     parent == NULL ? __glutRoot : parent->win,
  534.     x, y, width, height, 0,
  535.     window->vis->depth, InputOutput, window->vis->visual,
  536.     attribMask, &wa);
  537.   window->renderWin = window->win;
  538.  
  539.   window->width = width;
  540.   window->height = height;
  541.   window->forceReshape = True;
  542.  
  543.   window->parent = parent;
  544.   if (parent) {
  545.     window->siblings = parent->children;
  546.     parent->children = window;
  547.   } else {
  548.     window->siblings = NULL;
  549.   }
  550.   window->overlay = NULL;
  551.   window->children = NULL;
  552.   window->display = __glutDefaultDisplay;
  553.   window->reshape = __glutDefaultReshape;
  554.   window->mouse = NULL;
  555.   window->motion = NULL;
  556.   window->visibility = NULL;
  557.   window->passive = NULL;
  558.   window->entry = NULL;
  559.   window->special = NULL;
  560.   window->buttonBox = NULL;
  561.   window->dials = NULL;
  562.   window->spaceMotion = NULL;
  563.   window->spaceRotate = NULL;
  564.   window->spaceButton = NULL;
  565.   window->tabletMotion = NULL;
  566.   window->tabletButton = NULL;
  567.   window->tabletPos[0] = -1;
  568.   window->tabletPos[1] = -1;
  569.   window->keyboard = NULL;
  570.   window->shownState = 0;
  571.   window->visState = -1;  /* not VisibilityUnobscured,
  572.                              VisibilityPartiallyObscured, or
  573.                              VisibilityFullyObscured */
  574.   window->entryState = -1;  /* not EnterNotify or LeaveNotify */
  575.   window->damaged = 0;
  576.   window->workMask = GLUT_MAP_WORK;
  577.   window->desiredMapState = NormalState;
  578.   window->desiredConfMask = 0;
  579.   window->buttonUses = 0;
  580.   window->cursor = GLUT_CURSOR_INHERIT;
  581.   window->prevWorkWin = __glutWindowWorkList;
  582.   __glutWindowWorkList = window;
  583.   for (i = 0; i < GLUT_MAX_MENUS; i++) {
  584.     window->menu[i] = 0;
  585.   }
  586.   __glutWindowList[winnum] = window;
  587.   __glutSetWindow(window);
  588.   if (window->fakeSingle) {
  589.     glDrawBuffer(GL_FRONT);
  590.     glReadBuffer(GL_FRONT);
  591.   }
  592.   return window;
  593. }
  594.  
  595. static int
  596. findColormaps(GLUTwindow * window,
  597.   Window * winlist, Colormap * cmaplist, int num, int max)
  598. {
  599.   GLUTwindow *child;
  600.   int i;
  601.  
  602.   /* Do not allow more entries that maximum number of
  603.      colormaps! */
  604.   if (num >= max)
  605.     return num;
  606.   /* Is cmap for this window already on the list? */
  607.   for (i = 0; i < num; i++) {
  608.     if (cmaplist[i] == window->cmap)
  609.       goto normalColormapAlreadyListed;
  610.   }
  611.   /* Not found on the list; add colormap and window. */
  612.   winlist[num] = window->win;
  613.   cmaplist[num] = window->cmap;
  614.   num++;
  615.  
  616. normalColormapAlreadyListed:
  617.  
  618.   /* Repeat above but for the overlay colormap if there one. */
  619.   if (window->overlay) {
  620.     if (num >= max)
  621.       return num;
  622.     for (i = 0; i < num; i++) {
  623.       if (cmaplist[i] == window->overlay->cmap)
  624.         goto overlayColormapAlreadyListed;
  625.     }
  626.     winlist[num] = window->overlay->win;
  627.     cmaplist[num] = window->overlay->cmap;
  628.     num++;
  629.   }
  630. overlayColormapAlreadyListed:
  631.  
  632.   /* Recursively search children. */
  633.   child = window->children;
  634.   while (child) {
  635.     num = findColormaps(child, winlist, cmaplist, num, max);
  636.     child = child->siblings;
  637.   }
  638.   return num;
  639. }
  640.  
  641. void
  642. __glutEstablishColormapsProperty(GLUTwindow * window)
  643. {
  644.   static Atom wmColormapWindows = None;
  645.   Window *winlist;
  646.   Colormap *cmaplist;
  647.   Status status;
  648.   int maxcmaps, num;
  649.  
  650.   assert(!window->parent);
  651.   maxcmaps = MaxCmapsOfScreen(ScreenOfDisplay(__glutDisplay,
  652.       __glutScreen));
  653.   /* For portability reasons we don't use alloca for winlist
  654.      and cmaplist, but we could. */
  655.   winlist = (Window *) malloc(maxcmaps * sizeof(Window));
  656.   cmaplist = (Colormap *) malloc(maxcmaps * sizeof(Colormap));
  657.   num = findColormaps(window, winlist, cmaplist, 0, maxcmaps);
  658.   if (num < 2) {
  659.     /* Property no longer needed; remove it. */
  660.     wmColormapWindows = XInternAtom(__glutDisplay,
  661.       "WM_COLORMAP_WINDOWS", False);
  662.     if (wmColormapWindows == None) {
  663.       __glutWarning("Could not intern X atom for WM_COLORMAP_WINDOWS.");
  664.       return;
  665.     }
  666.     XDeleteProperty(__glutDisplay, window->win, wmColormapWindows);
  667.   } else {
  668.     status = XSetWMColormapWindows(__glutDisplay, window->win,
  669.       winlist, num);
  670.     /* XSetWMColormapWindows should always work unless the
  671.        WM_COLORMAP_WINDOWS property cannot be intern'ed.  We
  672.        check to be safe. */
  673.     if (status == False)
  674.       __glutFatalError("XSetWMColormapWindows returned False");
  675.   }
  676.   /* For portability reasons we don't use alloca for winlist
  677.      and cmaplist, but we could. */
  678.   free(winlist);
  679.   free(cmaplist);
  680. }
  681.  
  682. GLUTwindow *
  683. __glutToplevelOf(GLUTwindow * window)
  684. {
  685.   while (window->parent) {
  686.     window = window->parent;
  687.   }
  688.   return window;
  689. }
  690.  
  691. /* CENTRY */
  692. int
  693. glutCreateWindow(char *title)
  694. {
  695.   static int firstWindow = 1;
  696.   GLUTwindow *window;
  697.   XWMHints *wmHints;
  698.   Window win;
  699.   XTextProperty textprop;
  700.  
  701.   window = __glutCreateWindow(NULL,
  702.     __glutSizeHints.x, __glutSizeHints.y,
  703.     __glutInitWidth, __glutInitHeight);
  704.   win = window->win;
  705.   /* setup ICCCM properties */
  706.   textprop.value = (unsigned char *) title;
  707.   textprop.encoding = XA_STRING;
  708.   textprop.format = 8;
  709.   textprop.nitems = strlen(title);
  710.   wmHints = XAllocWMHints();
  711.   wmHints->initial_state =
  712.     __glutIconic ? IconicState : NormalState;
  713.   wmHints->flags = StateHint;
  714.   XSetWMProperties(__glutDisplay, win, &textprop, &textprop,
  715.   /* only put WM_COMMAND property on first window */
  716.     firstWindow ? __glutArgv : NULL,
  717.     firstWindow ? __glutArgc : 0,
  718.     &__glutSizeHints, wmHints, NULL);
  719.   firstWindow = 0;
  720.   XFree(wmHints);
  721.   XSetWMProtocols(__glutDisplay, win, &__glutWMDeleteWindow, 1);
  722.   return window->num + 1;
  723. }
  724.  
  725. int
  726. glutCreateSubWindow(int win, int x, int y, int width, int height)
  727. {
  728.   GLUTwindow *window, *toplevel;
  729.  
  730.   window = __glutCreateWindow(__glutWindowList[win - 1],
  731.     x, y, width, height);
  732.   toplevel = __glutToplevelOf(window);
  733.   if (toplevel->cmap != window->cmap) {
  734.     __glutPutOnWorkList(toplevel, GLUT_COLORMAP_WORK);
  735.   }
  736.   return window->num + 1;
  737. }
  738. /* ENDCENTRY */
  739.  
  740. static void
  741. destroyWindow(GLUTwindow * window,
  742.   GLUTwindow * initialWindow)
  743. {
  744.   GLUTwindow **prev, *cur, *parent, *siblings;
  745.  
  746.   /* Recursively destroy any children. */
  747.   cur = window->children;
  748.   while (cur) {
  749.     siblings = cur->siblings;
  750.     destroyWindow(cur, initialWindow);
  751.     cur = siblings;
  752.   }
  753.   /* Remove from parent's children list (only necessary for
  754.      non-initial windows and subwindows!). */
  755.   parent = window->parent;
  756.   if (parent && parent == initialWindow->parent) {
  757.     prev = &parent->children;
  758.     cur = parent->children;
  759.     while (cur) {
  760.       if (cur == window) {
  761.         *prev = cur->siblings;
  762.         break;
  763.       }
  764.       prev = &(cur->siblings);
  765.       cur = cur->siblings;
  766.     }
  767.   }
  768.   /* Unbind if bound to this window. */
  769.   if (window == __glutCurrentWindow) {
  770.     glXMakeCurrent(__glutDisplay, None, NULL);
  771.     __glutCurrentWindow = NULL;
  772.   }
  773.   /* Begin tearing down window itself. */
  774.   if (window->overlay) {
  775.     __glutFreeOverlayFunc(window->overlay);
  776.   }
  777.   XDestroyWindow(__glutDisplay, window->win);
  778.   glXDestroyContext(__glutDisplay, window->ctx);
  779.   if (window->colormap) {
  780.     /* Only color index windows have colormap data structure. */
  781.     __glutFreeColormap(window->colormap);
  782.   }
  783.   /* NULLing the __glutWindowList helps detect is a window
  784.      instance has been destroyed, given a window number. */
  785.   __glutWindowList[window->num] = NULL;
  786.  
  787.   /* Cleanup data structures that might contain window. */
  788.   cleanWindowWorkList(window);
  789.   cleanStaleWindowList(window);
  790.   /* Remove window from the "get window cache" if it is there. */
  791.   if (__glutWindowCache == window)
  792.     __glutWindowCache = NULL;
  793.  
  794.   XFree(window->vis);
  795.   free(window);
  796. }
  797.  
  798. /* CENTRY */
  799. void
  800. glutDestroyWindow(int win)
  801. {
  802.   GLUTwindow *window = __glutWindowList[win - 1];
  803.  
  804.   if (__glutMappedMenu && __glutMenuWindow == window) {
  805.     __glutFatalUsage("destroying menu window not allowed while menus in use");
  806.   }
  807.   /* if not a toplevel window... */
  808.   if (window->parent) {
  809.     /* destroying subwindows may change colormap requirements;
  810.        recalculate toplevel window's WM_COLORMAP_WINDOWS
  811.        property */
  812.     __glutPutOnWorkList(__glutToplevelOf(window->parent),
  813.       GLUT_COLORMAP_WORK);
  814.   }
  815.   destroyWindow(window, window);
  816. }
  817.  
  818. void
  819. glutSwapBuffers(void)
  820. {
  821.   GLUTwindow *window = __glutCurrentWindow;
  822.  
  823.   if (window->renderWin == window->win) {
  824.     if (__glutCurrentWindow->fakeSingle) {
  825.       /* Pretend the double buffered window is single buffered,
  826.          so treat glutSwapBuffers as a no-op */
  827.       return;
  828.     }
  829.   } else {
  830.     if (__glutCurrentWindow->overlay->fakeSingle) {
  831.       /* Pretend the double buffered overlay is single
  832.          buffered,  so treat glutSwapBuffers as a no-op. */
  833.       return;
  834.     }
  835.   }
  836.   glXSwapBuffers(__glutDisplay, __glutCurrentWindow->renderWin);
  837. }
  838. /* ENDCENTRY */
  839.  
  840. void
  841. __glutChangeWindowEventMask(long eventMask, Bool add)
  842. {
  843.   if (add) {
  844.     /* add eventMask to window's event mask */
  845.     if ((__glutCurrentWindow->eventMask & eventMask) !=
  846.       eventMask) {
  847.       __glutCurrentWindow->eventMask |= eventMask;
  848.       __glutPutOnWorkList(__glutCurrentWindow,
  849.         GLUT_EVENT_MASK_WORK);
  850.     }
  851.   } else {
  852.     /* remove eventMask from window's event mask */
  853.     if (__glutCurrentWindow->eventMask & eventMask) {
  854.       __glutCurrentWindow->eventMask &= ~eventMask;
  855.       __glutPutOnWorkList(__glutCurrentWindow,
  856.         GLUT_EVENT_MASK_WORK);
  857.     }
  858.   }
  859. }
  860.  
  861. void
  862. glutDisplayFunc(GLUTdisplayCB displayFunc)
  863. {
  864.   /* XXX Remove the warning after GLUT 3.0. */
  865.   if (!displayFunc)
  866.     __glutFatalError("NULL display callback not allowed in GLUT 3.0; update your code.");
  867.   __glutCurrentWindow->display = displayFunc;
  868. }
  869.  
  870. void
  871. glutKeyboardFunc(GLUTkeyboardCB keyboardFunc)
  872. {
  873.   __glutChangeWindowEventMask(KeyPressMask,
  874.     keyboardFunc != NULL || __glutCurrentWindow->special != NULL);
  875.   __glutCurrentWindow->keyboard = keyboardFunc;
  876. }
  877.  
  878. void
  879. glutSpecialFunc(GLUTspecialCB specialFunc)
  880. {
  881.   __glutChangeWindowEventMask(KeyPressMask,
  882.     specialFunc != NULL || __glutCurrentWindow->keyboard != NULL);
  883.   __glutCurrentWindow->special = specialFunc;
  884. }
  885.  
  886. void
  887. glutMouseFunc(GLUTmouseCB mouseFunc)
  888. {
  889.   if (__glutCurrentWindow->mouse) {
  890.     if (!mouseFunc) {
  891.       /* previous mouseFunc being disabled */
  892.       __glutCurrentWindow->buttonUses--;
  893.       __glutChangeWindowEventMask(
  894.         ButtonPressMask | ButtonReleaseMask,
  895.         __glutCurrentWindow->buttonUses > 0);
  896.     }
  897.   } else {
  898.     if (mouseFunc) {
  899.       /* previously no mouseFunc, new one being installed */
  900.       __glutCurrentWindow->buttonUses++;
  901.       __glutChangeWindowEventMask(
  902.         ButtonPressMask | ButtonReleaseMask, True);
  903.     }
  904.   }
  905.   __glutCurrentWindow->mouse = mouseFunc;
  906. }
  907.  
  908. void
  909. glutMotionFunc(GLUTmotionCB motionFunc)
  910. {
  911.   /* Hack.  Some window managers (4Dwm by default) will mask
  912.      motion events if the client is not selecting for button
  913.      press and release events. So we select for press and
  914.      release events too (being careful to use reference
  915.      counting).  */
  916.   if (__glutCurrentWindow->motion) {
  917.     if (!motionFunc) {
  918.       /* previous mouseFunc being disabled */
  919.       __glutCurrentWindow->buttonUses--;
  920.       __glutChangeWindowEventMask(
  921.         ButtonPressMask | ButtonReleaseMask,
  922.         __glutCurrentWindow->buttonUses > 0);
  923.     }
  924.   } else {
  925.     if (motionFunc) {
  926.       /* previously no mouseFunc, new one being installed */
  927.       __glutCurrentWindow->buttonUses++;
  928.       __glutChangeWindowEventMask(
  929.         ButtonPressMask | ButtonReleaseMask, True);
  930.     }
  931.   }
  932.   /* Real work of selecting for passive mouse motion.  */
  933.   __glutChangeWindowEventMask(
  934.     Button1MotionMask | Button2MotionMask | Button3MotionMask,
  935.     motionFunc != NULL);
  936.   __glutCurrentWindow->motion = motionFunc;
  937. }
  938.  
  939. void
  940. glutPassiveMotionFunc(GLUTpassiveCB passiveMotionFunc)
  941. {
  942.   __glutChangeWindowEventMask(PointerMotionMask,
  943.     passiveMotionFunc != NULL);
  944.  
  945.   /* Passive motion also requires watching enters and leaves so
  946.      that a fake passive motion event can be generated on an
  947.      enter. */
  948.   __glutChangeWindowEventMask(EnterWindowMask | LeaveWindowMask,
  949.     __glutCurrentWindow->entry != NULL || passiveMotionFunc != NULL);
  950.  
  951.   __glutCurrentWindow->passive = passiveMotionFunc;
  952. }
  953.  
  954. void
  955. glutEntryFunc(GLUTentryCB entryFunc)
  956. {
  957.   __glutChangeWindowEventMask(EnterWindowMask | LeaveWindowMask,
  958.     entryFunc != NULL || __glutCurrentWindow->passive);
  959.   __glutCurrentWindow->entry = entryFunc;
  960.   if (!entryFunc) {
  961.     __glutCurrentWindow->entryState = -1;
  962.   }
  963. }
  964.  
  965. void
  966. glutVisibilityFunc(GLUTvisibilityCB visibilityFunc)
  967. {
  968.   __glutChangeWindowEventMask(VisibilityChangeMask,
  969.     visibilityFunc != NULL);
  970.   __glutCurrentWindow->visibility = visibilityFunc;
  971.   if (!visibilityFunc) {
  972.     /* make state invalid */
  973.     __glutCurrentWindow->visState = -1;
  974.   }
  975. }
  976.  
  977. void
  978. glutReshapeFunc(GLUTreshapeCB reshapeFunc)
  979. {
  980.   if (reshapeFunc) {
  981.     __glutCurrentWindow->reshape = reshapeFunc;
  982.   } else {
  983.     __glutCurrentWindow->reshape = __glutDefaultReshape;
  984.   }
  985. }
  986.